Skip to content

fix(compose): preserve project env vars when compose specifies empty passthrough placeholders - #616

Merged
Hydralerne merged 2 commits into
oblien:mainfrom
chbndrhnns:fix/compose-env-passthrough
Aug 19, 2026
Merged

fix(compose): preserve project env vars when compose specifies empty passthrough placeholders#616
Hydralerne merged 2 commits into
oblien:mainfrom
chbndrhnns:fix/compose-env-passthrough

Conversation

@chbndrhnns

Copy link
Copy Markdown
Contributor

Problem

When a Docker Compose file defines environment variables using passthrough or default syntax (such as MY_VAR: ${MY_VAR:-} or MY_VAR: ${MY_VAR}), the compose parser imports these unset variables into the service's inline environment as "" (empty string).

During deployment, mergeServiceDeployEnv layers project -> inline -> service. Because the inline environment had MY_VAR: "", it clobbered any configured non-empty project-level environment variable with an empty string.

Closes #614


Solution

  • In mergeServiceDeployEnv, when merging the inline compose environment over the project environment, skip empty string values ("") if the project environment already defines a non-empty value for that key.
  • Explicit non-empty inline literals (e.g. PORT: "3000") and explicit service-scoped environment overrides still take precedence as intended.
  • Added comprehensive unit tests in apps/api/test/modules/deployments/compose-env-passthrough.test.ts to verify and defend this behavior.

chbndrhnns and others added 2 commits August 18, 2026 09:19
…passthrough placeholders

Do not let empty inline compose environment values (originating from
unpopulated compose passthrough expressions like ${VAR:-} or ${VAR})
clobber configured non-empty project-level environment variables during
deploy env merge.

Fixes oblien#614
…n step

Review follow-ups on the oblien#614 fix.

- Move the layering to `compose/service-env-layers.ts` and give the rule a
  name (`inlineEmptyDefers`). Two surfaces spelled it out by hand; they now
  import it.
- Log every substitution. The service Env tab and the deploy wizard both go
  on showing the empty value the merge ignored, so the deploy log was the
  only surface that could explain the container — and it said nothing. Names
  only, never values, matching `resolveEnvPublicUrls` just below it.
- `describeRestorePlan` built its per-service overrides as a raw spread of
  the service row, under a comment asserting it matched the deploy merge. It
  no longer did, so the rollback confirm dialog reported a `frozen-wins`
  revert plus a `scopeAmbiguous` warning for every compose passthrough key
  the rollback leaves alone. It applies the same deferral now.
- Correct the docstring, which still stated the old flat precedence, and
  write down what the rule costs: an authored empty no longer clears a
  configured value, and an empty service-scoped row is the escape hatch.
- Tests for the frozen layer (the combination every non-rollback deploy
  has), the rollback replay, the accepted trade-off, the escape hatch, and
  the gap a value-shaped rule cannot close — `postgres://${U}:${P}@db/${D}`
  resolves to non-empty garbage and still wins.
- Note in `getAppConnectionView` that it is NOT the deploy merge: it omits
  the project layer entirely. Adding it changes what every existing app
  connection resolves to, so that is left for its own change.
@Hydralerne
Hydralerne merged commit 92fffe4 into oblien:main Aug 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compose passthrough environment variables (${VAR:-}) import as empty string and clobber project-level env vars

2 participants